package com.knight.spring.framework.aop.intercept;

import com.knight.spring.framework.aop.adapter.KnightJoinPoint;
import com.sun.org.apache.regexp.internal.RE;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class KnightMethodInvocation implements KnightJoinPoint {

    protected final Object proxy;

    protected final Object target;

    protected final Method method;

    protected Object[] arguments;

    private final Class<?> targetClass;

    protected final List<?> interceptorsAndDynamicMethodMatchers;

    private int currentInterceptorIndex = -1;

    public KnightMethodInvocation(
            Object proxy,  Object target, Method method,  Object[] arguments, Class<?> targetClass,
            List<Object> interceptorsAndDynamicMethodMatchers) {

        this.proxy = proxy;
        this.target = target;
        this.targetClass = targetClass;
        this.method = method;
        this.arguments = arguments;
        this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
    }


    public Object proceed() throws Throwable{
        // We start with an index of -1 and increment early.
        if (interceptorsAndDynamicMethodMatchers==null ||
                this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return invokeJoinpoint();
        }

        Object interceptorOrInterceptionAdvice =
                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        if (interceptorOrInterceptionAdvice instanceof KnightMethodInterceptor) {
            // Evaluate dynamic method matcher here: static part will already have
            // been evaluated and found to match.
            KnightMethodInterceptor dm =
                    (KnightMethodInterceptor) interceptorOrInterceptionAdvice;
                return dm.invoke(this);
        }
        else {
            return proceed();
        }
    }

    protected Object invokeJoinpoint() {
        try {
            return method.invoke(target,arguments);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public Object getThis() {
        return this.target;
    }

    private Map<String,Object> userAttribute = new HashMap<>();


    @Override
    public void setUserAttribute(String key, Object value) {
        if (value!=null) {
            userAttribute.put(key, value);
        }else{
            userAttribute.remove(key);
        }
    }

    @Override
    public Object getUserAttribute(String key) {
        return userAttribute.get(key);
    }

    @Override
    public Method getMethod() {
        return method;
    }

    @Override
    public Object[] getArguments() {
        return arguments;
    }
}
